package com.wang.socket;

import com.wang.util.ModBusUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.Arrays;
import java.util.List;

/**
 * 自定义解码器
 *
 * @author wcy 停用
 */
public class CustomDecoder extends ByteToMessageDecoder {

    private final int BASE_LENGTH = 1;

    private String heart = "METER";
    private String reg = "DSDEVREG";
    int num = 0;

    /**
     * 十六进制字符串数组解析
     *
     * @param channelHandlerContext
     * @param in                    传入数据
     * @param list                  添加解码消息
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> list) throws Exception {


        int beginIndex;//记录包开始位置
        //定义最终数据
        byte[] finalData = null;


        while (true) {

            //获取包头开始的index
            beginIndex = in.readerIndex();
            // 将当前的readerIndex备份到markedReaderIndex中
            in.markReaderIndex();


            //获取字节可读取长度
            int reablesLength = in.readableBytes();

            //判断是不是心跳包  json modbus 通用
            //---------------------------------
            //1、判断是不是心跳包
            //若数据包小于心跳包数量  直接退出
            if (reablesLength < heart.getBytes().length) {
                return;
            }
            //先读前心跳包长度 判断是是不是心跳包数据
            byte[] bytes = new byte[heart.getBytes().length];
            in.readBytes(bytes);

            String data = new String(bytes, "UTF-8");
            if (data.equals(heart)) {

                String ctxData = new String(bytes, "UTF-8");
                // System.out.println("心跳包:"+new String(ctxData +":"+(num++));
                //添加数据

                list.add(ctxData);
                //回收已读字节
                in.discardReadBytes();
                return;
            }


            //2、判断是不是注册包
            //--------------------------------------------
            //重置字节读取下标
            in.resetReaderIndex();
            //若数据包小于注册包数量  直接退出
            if (reablesLength < reg.getBytes().length) {
                return;
            }
            //读取前8位字节 判断是不是注册包数据
            byte[] bytes2 = new byte[reg.getBytes().length];
            in.readBytes(bytes2);
            String data2 = new String(bytes2, "UTF-8");
            if (data2.equals(reg)) {
                //继续往下读  读到；结束
                //定义下标
                int index = 0;
                //定义包含;的标志
                boolean containFlag = false;

                for (int i = 0; i < reablesLength - reg.getBytes().length; i++) {

                    //读取一个字节
                    byte[] bytesFlag = new byte[1];
                    in.readBytes(bytesFlag);
                    String flag = new String(bytesFlag, "UTF-8");

                    if (flag.equals(";")) {
                        index = i;
                        containFlag = true;
                        break;
                    }
                }
                if (containFlag) {
                    //重置读取字节位置
                    in.resetReaderIndex();
                    //读取完整的注册包数据
                    byte[] regDatas = new byte[reg.getBytes().length + index + 1];
                    in.readBytes(regDatas);
                    String ctxData = new String(regDatas, "UTF-8");
                    //System.out.println(ctxData);

                    //添加数据
                    list.add(ctxData.replace(";", ""));
                    //回收已读字节
                    in.discardReadBytes();
                    return;
                } else {
                    //如果不包含 代表数据不全，返回读的指针 等待下一次的数据到期在读
                    in.readerIndex(beginIndex);
                    return;
                }


            }


            //3、判断是不是json数据
            //--------------------------------
            //重置字节读取下标
            //重置字节读取下标
            in.resetReaderIndex();

            //方式  读取左大括号 +1   读取到右括号-1
            //先读取第一个字节 是否是 { 如果是依次读字节
            byte[] byteleft = new byte[1];
            in.readBytes(byteleft);
            String firstData = new String(byteleft, "UTF-8");
            if (firstData.equals("{")) {
                //下标
                int indexFlag = 0;
                int intFlag = 0;


                indexFlag++;
                intFlag++;
                //依次往下读
                //读取一个字节 -1 代表已经读过一个字节
                for (int i = 0; i < reablesLength - 1; i++) {

                    byte[] bytesFlag = new byte[1];
                    in.readBytes(bytesFlag);
                    indexFlag++;
                    String flag = new String(bytesFlag, "UTF-8");
                    if (flag.equals("{")) {
                        //标识+1
                        intFlag++;
                    }
                    if (flag.equals("}")) {
                        //标识-1
                        intFlag--;
                    }
                    if (intFlag == 0) {
                        //如果标识为0 查询到所有json数据
                        break;
                    }
                }

                if (intFlag == 0) {


                    //截取数据
                    //重置字节读取下标
                    in.resetReaderIndex();
                    byte[] jsonBytes = new byte[indexFlag];
                    in.readBytes(jsonBytes);
                    String jsonData = new String(jsonBytes, "UTF-8");

                    //添加数据
                    list.add(jsonData);
                    //回收已读字节
                    in.discardReadBytes();
                    //System.out.println("json数据:"+jsonData);
                    return;

                } else {

                    //如果不包含 代表数据不全，返回读的指针 等待下一次的数据到期在读
                    in.readerIndex(beginIndex);
                    return;
                }


            }


            //最后一步 modbus数据读取
            //重置读取位
            in.resetReaderIndex();
            //modbus数据读取前3个字节 获取数据长度
            byte[] mdatas = new byte[3];
            in.readBytes(mdatas);
            //截取数据位长度
            String mlength = bytes2HexString(mdatas).split(" ")[2];

            //重置读取位
            in.resetReaderIndex();

            Integer length = hex2decimal(mlength);

            int totalDataLength = 3 + length + 2;  //地址 +功能 +数据长度  3   加上数据长度 +  校验位长度2
            //如果可读数据位长度小于  数据位长度  返回 等待下一次数据到期在读
            if (reablesLength < totalDataLength) {
                in.readerIndex(beginIndex);
                return;
            }
            byte[] mdatas2 = new byte[totalDataLength];

            in.readBytes(mdatas2);
        /*    String baseData =  ModBusUtil.bytes2HexString(mdatas2);
            list.add(baseData);
            //System.out.println("数据:"+bytes2HexString(mdatas2));
            //回收已读字节
            in.discardReadBytes();
            break;*/


            //计算modbus数据合法性
            //先将bytes 转换成16进制字符串
            String baseData = ModBusUtil.bytes2HexString(mdatas2);
            String baseDataStr = baseData.replaceAll(" ", "");
            String[] baseDatas = baseData.split(" ");

            //要校验的数据
            String[] checkDatas = Arrays.copyOfRange(baseDatas, 0, baseDatas.length - 2);
            StringBuffer sb1 = new StringBuffer();
            for (String s : checkDatas) {
                sb1.append(s);
            }

            byte[] checkBytes = ModBusUtil.HexString2Bytes(sb1.toString());


            //计算crc值
            String checkCrc2 = ModBusUtil.getCRC3(checkBytes);
            //截取原有crc值
            String checkCrc1 = baseDataStr.substring(baseDataStr.length() - 4);

            if (checkCrc1.equals(checkCrc2)) {
                //添加数据
                list.add(baseData);
                //System.out.println("数据:"+bytes2HexString(mdatas2));
                //回收已读字节
                in.discardReadBytes();
                break;
                //System.out.println("数据:" + new String(finData, "UTF-8"));
            } else {
                break;

            }

        }


    }


    /*
     * 字节数组转16进制字符串
     */
    public static String bytes2HexString(byte[] b) {
        String r = "";
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            r += hex.toUpperCase() + " ";
        }
        return r;
    }

    // 16进制字符串转换10进制
    public static int hex2decimal(String hex) {
        return Integer.parseInt(hex, 16);
    }


}


